using System;
using System.Collections.Generic;
using System.Reflection;
using AntEater.ComponentModel.Configuration;

namespace AntEater.Runtime.Services
{
    /// <summary>
    /// Provides a factory for retrieving a runtime service.
    /// </summary>
    public class RuntimeServiceProvider : IServiceProvider
    {
        private RuntimeServiceElementCollection _runtimeServiceConfiguration;

        /// <summary>
        /// Initializes a new instance of the <see cref="RuntimeServiceProvider"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="runtimeServiceConfiguration">The runtime service configuration.</param>
        public RuntimeServiceProvider(RuntimeServiceContext context, RuntimeServiceElementCollection runtimeServiceConfiguration) {
            this._context = context;
            this._runtimeServiceConfiguration = runtimeServiceConfiguration;
            this._reusableRuntimeServices = new Dictionary<Type, IRuntimeService>();
        }

        private RuntimeServiceContext _context;

        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <value>The context.</value>
        public RuntimeServiceContext Context {
            get { return _context; }
        }

        private Dictionary<Type, IRuntimeService> _reusableRuntimeServices;

        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <param name="serviceType">An object that specifies the type of service object to get.</param>
        /// <returns>
        /// A service object of type serviceType.-or- null if there is no service object of type serviceType.
        /// </returns>
        public object GetService(Type serviceType) {

            if (serviceType == null) { throw new ArgumentNullException("serviceType"); }

            IRuntimeService runtimeService = null;

            // Return service if it allready exist or create a new instance
            if (_reusableRuntimeServices.ContainsKey(serviceType) == true) {
                runtimeService = _reusableRuntimeServices[serviceType];

            } else {
                // Read the runtime service type of the specified service interface from the configuration
                RuntimeServiceElement element = _runtimeServiceConfiguration[serviceType.Name];

                // Load the assembly and get the type
                Assembly assembly = Assembly.Load(element.Assembly);
                Type type = assembly.GetType(element.Type);

                // Create an instance of the type
                runtimeService = (IRuntimeService)Activator.CreateInstance(type);

                //Initialize the presenter with the current context
                if (runtimeService != null) {
                    runtimeService.Initialize(this.Context);

                    // Add runtime service to the reusable services collection
                    if (runtimeService.IsReusable) {
                        _reusableRuntimeServices.Add(serviceType, runtimeService);
                    }
                }
            }

            return runtimeService;
        }
    }
}
